package com.mybpm.core.definition

import grails.core.GrailsApplication
import grails.util.Holders
import org.slf4j.Logger
import org.slf4j.LoggerFactory

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

/**
 * 流程加载服务
 */
@Service
public class ProcessLoadService {
    private static final Logger log = LoggerFactory.getLogger(ProcessLoadService.class)

    @Autowired
    ProcessParseService processParseService
    @Autowired
    GrailsApplication grailsApplication = Holders.grailsApplication

    private String processesPath = "/process/**/*.xml"

    private static final Map<String, Map> processes = [:]
    private static final Map<String, String> processNames = [:]

    /**
     * 加载流程
     */
    public Map<String, Map> loadXml(String bpmPath) {
        log.debug("----------loadXml")
        // TODO xml从数据库中来，数据库中的xml是admin平台上传的
        def resources = grailsApplication.mainContext.getResources("file:" + bpmPath + processesPath)
        resources.each { resource ->
            log.debug("----------xmlFile={}", resource.file.path)
            String xml = resource.file.getText('UTF-8')
            log.debug("----------loadXml{}", xml)
            Map proc = loadProcess(xml.replaceAll(">\\s+<", "><").replaceAll("[\\r\\n]", ""), null, true)
            processes[proc.process.id] = proc
            if (proc.process.name) {
                if (processNames[proc.process.name])
                    log.warn("这里发现了一个重名的流程，流程名称={}", proc.process.name)
                processNames[proc.process.name] = proc.process.id
            }
        }
        return processes
    }

    /**
     * 根据流程名称查找流程id。此方法不保证正确性，名称会重复
     */
    public String processName2Id(String processName) {
        return processNames[processName];
    }

    /**
     * 获取流程定义
     *
     * @param processId 流程定义id
     */
    public XmlProcess getXmlProcess(String processId) {
        return processes[processId].process
    }

    /**
     * 获取流程起始环节定义
     *
     * @param processId 流程定义id
     */
    public XmlNode getStartNode(String processId) {
        return processes[processId].start
    }

    /**
     * 获取流程结束环节定义
     *
     * @param processId 流程定义id
     */
    public XmlNode getEndNode(String processId) {
        return processes[processId].end
    }

    /**
     * 获取流程环节定义
     *
     * @param processId 流程定义id
     * @param nodeId 环节定义id
     */
    public XmlNode getXmlNode(String processId, String nodeId) {
        if (nodeId.equals(processes[processId].start.id))
            return processes[processId].start
        else if (nodeId.equals(processes[processId].end.id))
            return processes[processId].end
        else return processes[processId].nodes.get(nodeId)
    }

    /**
     * 获取流程环节id。此方法不保证正确性，名称会重复
     *
     * @param processId 流程定义id
     * @param nodeName 环节定义name
     */
    public String nodeName2Id(String processId, String nodeName) {
        if (nodeName.equals(processes[processId].start.name))
            return processes[processId].start.id
        else if (nodeName.equals(processes[processId].end.name))
            return processes[processId].end.id
        else {
            XmlNode node = null
            processes[processId].nodes.each {
                if (nodeName.equals(it.value.name))
                    return node = it.value
            }
            return node?.id
        }
    }

    /**
     * 获取流程路径定义
     *
     * @param processId 流程定义id
     * @param transitionId 路径定义id
     */
    public XmlTransition getXmlTransition(String processId, String transitionId) {
        return processes[processId].transitions.get(transitionId)
    }

    /**
     * 获取流程路径id。此方法不保证正确性，名称会重复
     *
     * @param processId 流程定义id
     * @param transitionName 路径定义name
     */
    public String transitionName2Id(String processId, String transitionName) {
        XmlTransition transition = null
        processes[processId].transitions.each {
            if (transitionName.equals(it.value.name))
                return transition = it.value
        }
        return transition?.id
    }

    /**
     * 解析流程定义并缓存<p>
     *
     * @param xml 流程定义文件
     * @param signavioXml 流程图计算文件
     * @param update 是否更新数据库
     */
    private Map loadProcess(String xml, String signavioXml, boolean update = false) {
        Map proc = processParseService.parse(xml)
        if (update)
            save2DB(proc, xml)
        log.debug('------{}', proc)
        return proc
    }

    /**
     * 保存到数据库<p>
     */
    private void save2DB(Map proc, String xml) {
        // 保存流程
        Process process = saveProcess2DB(proc, xml)

        // 保存环节和路径
        saveNode2DB(proc.start, process)
        ((Map<String, XmlNode>) proc.nodes).each { k, v ->
            saveNode2DB(v, process)
        }
        saveNode2DB(proc.end, process)
    }

    /**
     * 保存流程到数据库<p>
     */
    private Process saveProcess2DB(Map proc, String xml) {
        Process process = Process.findByPidAndRevision(proc.process.id, proc.process.version)
        if (!process) process = new Process(pid: proc.process.id, revision: proc.process.version)
        process.name = proc.process.name
        process.displayName = proc.process.displayName
        process.type = proc.process.type
        process.description = proc.process.description
        process.xml = xml
        process.save()
        return process
    }

    /**
     * 保存环节和路径到数据库<p>
     */
    private void saveNode2DB(XmlNode xmlNode, Process process) {
        Node node = Node.findByNid(xmlNode.id)
        if (!node) node = new Node(nid: xmlNode.id)
        node.name = xmlNode.name
        node.displayName = xmlNode.displayName
        node.type = xmlNode.type
        node.process = process
        node.save()
        // 保存路径
        xmlNode.transitions.each { it ->
            saveTransition2DB(it, node)
        }
    }

    /**
     * 保存路径到数据库<p>
     */
    private void saveTransition2DB(XmlTransition xmlTransition, Node node) {
        Transition transition = Transition.findByTid(xmlTransition.id)
        if (!transition) transition = new Transition(tid: xmlTransition.id)
        transition.name = xmlTransition.name
        transition.displayName = xmlTransition.displayName
        transition.type = xmlTransition.type
        transition.node = node
        transition.save()
    }
}